package com.opennews.openplatform


import grails.gorm.transactions.Transactional
import groovy.transform.CompileStatic
import org.springframework.beans.factory.annotation.Value
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest

@Transactional
class FileService {
    @Value('${openplatform.resource.upload.rootFilePath}')
    String rootFilePath

    @Value('${openplatform.resource.avatarDirectoryName}')
    String avatarDirectoryName

    UserService userService

    /**
     * Uploads the avatar by using the file content in base64 format.
     * @param userId: The string of the user'id.
     * @param originalFilename: The string of original filename of avatar.
     * @param base64Content: The file content in base64 format.
     * @return The string of new avatar full url.
     */
    @CompileStatic
    String uploadAvatarByBase64(String userId, String originalFilename, String base64Content) {
        // Generates the new filename with timestamp.
        def filename = generateFilename(originalFilename)

        // Decodes the input base64 content.
        def bytes = base64Content.decodeBase64()

        // Gets the avatar file saved path.
        def uploadAvatarPath = getUploadedAvatarPath()

        // The file instance of new avatar.
        def uploadAvatarFile = new File(uploadAvatarPath, filename)

        // Writes the bytes to new avatar file.
        uploadAvatarFile.withOutputStream {
            try {
                it.write(bytes)
                it.flush()
            } finally {
                it.close()
            }
        }

        // Updates user avatar in the data source and removes old avatar file.
        updateUserAvatar(userId, uploadAvatarPath, filename)

        // Returns the filename as output.
        // The filename type is GString. That's why toString used here.
        return userService.getAvatarUrl(filename.toString())
    }

    /**
     * Uploads the avatar by using the uploaded file.
     * @param userId: The string of the user'id.
     * @param file: The uploaded file of avatar.
     * @return The string of new avatar full url.
     */
    String uploadAvatar(String userId, StandardMultipartHttpServletRequest.StandardMultipartFile file) {
        // https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/multipart/MultipartFile.html
        // Gets the file original file name.
        def originalFilename = file.getOriginalFilename()

        // Generates the new filename with timestamp.
        def filename = generateFilename(originalFilename)

        // Checks if the file is empty.
        if (!file.isEmpty()) {
            // Gets the avatar file saved path.
            def uploadAvatarPath = getUploadedAvatarPath()

            // The file instance of new avatar.
            def uploadAvatarFile = new File(uploadAvatarPath, filename)

            // Transfers the file to the path.
            file.transferTo(uploadAvatarFile)

            // Updates user avatar in the data source and removes old avatar file.
            updateUserAvatar(userId, uploadAvatarPath, filename)

            // Returns the filename as output.
            // The filename type is GString. That's why toString used here.
            return userService.getAvatarUrl(filename.toString())
        } else {
            return ""
        }
    }

    /**
     * Uploads file with scaling width.
     * @param file: The uploaded file.
     * @param directoryName: The name of the directory where the file is uploaded to.
     * @param targetWidth: The int of the target image width. If scaling is needed please specify this value.
     * @return The string of new upload filename.
     */
    String uploadFile(StandardMultipartHttpServletRequest.StandardMultipartFile file, String directoryName, int targetWidth) {
        // https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/multipart/MultipartFile.html
        // Gets the file original file name.
        def originalFilename = file.getOriginalFilename()

        // Generates the new filename with timestamp.
        def filename = generateFilename(originalFilename)

        // Gets file extension.
        def extension = getFileExtension(filename)

        // Checks if the file is empty.
        if (!file.isEmpty()) {
            // Gets the upload file saved path.
            def uploadPath = getUploadedDirectoryPath(directoryName)

            // The file instance of new upload.
            def uploadFile = new File(uploadPath, filename)

            // Transfers the file to the path.
            file.transferTo(uploadFile)

            // Returns the filename as output.
            return filename
        } else {
            return ""
        }
    }

    /**
     * Uploads file without scaling.
     * @param file: The uploaded file.
     * @param directoryName: The name of the directory where the file is uploaded to.
     * @return The string of new upload filename.
     */
    String uploadFile(StandardMultipartHttpServletRequest.StandardMultipartFile file, String directoryName) {
        return uploadFile(file, directoryName, -1)
    }

    /**
     * For demo purpose only at this moment.
     * @param files
     * @return
     */
    List<String> uploadFiles(List<StandardMultipartHttpServletRequest.StandardMultipartFile> files) {
        def newFilenames = []

        // def d = request.multipartFiles
        // https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/multipart/MultipartFile.html
        files.each { file ->
            // Gets the file original file name.
            def filename = file.getOriginalFilename()

            // Gets the last position of '.'. This is for getting the file extension.
            def lastDotIndex = filename.lastIndexOf(".")

            // If the filename contains extension then we need to keep it.
            if (lastDotIndex > -1) {
                filename = new Date().format("yyyyMMddHHmmsss-" + SharedUtil.nextRandomIntInRange(1000, 9999).toString()) + filename.substring(lastDotIndex)
            } else {
                filename = new Date().format("yyyyMMddHHmmsss-" + SharedUtil.nextRandomIntInRange(1000, 9999).toString())
            }

            // Checks if the file is empty.
            if (!file.isEmpty()) {
                File tempFile = new File("D:/" + filename)
                file.transferTo(tempFile)

                // Saves the temp file path.
                newFilenames << tempFile.path
            }
        }

        return newFilenames
    }

    void delete(String filename) {
        File file = new File(getUploadedAvatarPath(), filename)

        if (file.exists()) {
            file.delete()
        }
    }

    /**
     * Generates the new avatar filename based on original file extension name.
     * @param originalFilename: The string of uploaded file original filename.
     * @return The string of new generated filename with original file extension name.
     */
    private String generateFilename(String originalFilename) {
        // Generates the new filename with timestamp.
        def filename = "${DateHelper.getCurrentDate().format(DateHelper.LONG_TIMESTAMP)}-${SharedUtil.nextRandomIntInRange(100000, 999999)}"

        // Gets file extension.
        def extension = getFileExtension(originalFilename)

        // If the filename contains extension then we need to keep it.
        if (!SharedUtil.isNullOrEmpty(extension)) {
            filename += ".${extension}"
        }

        return filename
    }

    /**
     * Gets extension from filename.
     * @param filename: The string of input filename.
     * @return The string of file extension. Example: "jpg". Please notice the leading dot is NOT included.
     */
    private String getFileExtension(String filename) {
        // Gets the last position of '.'. This is for getting the file extension.
        def lastDotIndex = filename.lastIndexOf(".")

        // If the filename contains extension.
        if (lastDotIndex > -1) {
            return filename.substring(lastDotIndex + 1)
        }

        return ""
    }

    /**
     * Gets file instance of uploaded avatar path.
     * @return The file instance of the new avatar path which avatar files saved to.
     */
    private File getUploadedAvatarPath() {
        return getUploadedDirectoryPath(avatarDirectoryName)
    }

    /**
     * Gets file instance of uploaded directory path.
     * @return The file instance of the new directory path which uploaded files saved to.
     */
    private File getUploadedDirectoryPath(String directoryName) {
        // Gets the upload avatar file path.
        def uploadPath = new File(rootFilePath, directoryName)

        // If the path does not exist then creates it.
        if (!uploadPath.exists()) {
            uploadPath.mkdirs()
        }

        return uploadPath
    }

    /**
     * Updates user's avatar filename and deletes old avatar file.
     * @param userId: The string of the user id.
     * @param uploadAvatarPath: The file instance of the path where avatar files saved to.
     * @param filename: The new avatar filename.
     */
    private void updateUserAvatar(String userId, File uploadAvatarPath, String filename) {
        // Gets the user for deleting old avatar file and updating new one.
        def user = User.get(userId)

        // Gets current user avatar filename.
        def currentAvatarFilename = user.avatarFilename

        // Deletes the old avatar file if needed.
        if (!SharedUtil.isNullOrEmpty(currentAvatarFilename)) {
            def currentAvatarFile = new File(uploadAvatarPath, currentAvatarFilename)

            if (currentAvatarFile.exists()) {
                currentAvatarFile.delete()
            }
        }

        // Saves the new avatar filename.
        user.avatarFilename = filename
        user.save()
    }
}